package com.zshy.base.core.utils;

import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.zshy.base.core.contract.exception.JsonConversionException;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * json工具类（jackson）
 *
 * @author zhouhengzhe
 */
@Slf4j
@UtilityClass
public class JsonKit {

    private static final String DATE_PATTERN = "yyyy-MM-dd";
    private static final String TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
    public static final ObjectMapper DEFAULT_OBJECT_MAPPER = defaultObjectMapper();

    /**
     * 默认的ObjectMapper配置
     * <p>
     * defaultObjectMapper方法创建并配置了一个ObjectMapper实例，用于处理JSON数据的序列化和反序列化。它禁用了未知属性的反序列化失败，设置了日期格式，注册了Java 8时间模块，并设置了时区。
     *
     * @return
     */
    public ObjectMapper defaultObjectMapper() {
        // 创建一个ObjectMapper实例
        ObjectMapper objectMapper = new ObjectMapper();
        // 禁用未知属性的反序列化失败
        objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        // 设置日期格式
        objectMapper.setDateFormat(new BaseSimpleDateFormat());
        // 创建一个JavaTimeModule实例
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        // 为LocalDate类添加序列化器和反序列化器
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DATE_PATTERN)));
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DATE_PATTERN)));
        // 为LocalDateTime类添加序列化器和反序列化器
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(TIME_PATTERN)));
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(TIME_PATTERN)));
        // 注册JavaTimeModule到ObjectMapper中
        objectMapper.registerModule(javaTimeModule);
        // 设置时区为上海时区
        objectMapper.setTimeZone(TimeZone.getTimeZone(ZoneId.of("Asia/Shanghai")));
        // 设置序列化时只包含非null的属性
        objectMapper.setSerializationInclusion(Include.NON_NULL);
        // 返回配置好的ObjectMapper实例
        return objectMapper;
    }

    /**
     * Redis专用的ObjectMapper配置
     * <p>
     * redisObjectMapper方法创建了一个专用于Redis的ObjectMapper实例，它启用了所有属性的可见性，激活了默认的类型信息，并配置了一些其他特性。
     * <p>
     * 注意：如果需要在序列化过程中返回异常错误，请不要使用此方法
     *
     * @return 配置好的ObjectMapper实例
     */
    public ObjectMapper redisObjectMapper() {
        // 调用defaultObjectMapper方法获取默认的ObjectMapper实例
        ObjectMapper objectMapper = defaultObjectMapper();
        // 设置所有属性的可见性为任意
        objectMapper.setVisibility(PropertyAccessor.ALL, Visibility.ANY);
        // 激活默认的类型信息，并设置为非final，使用包装数组形式
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, DefaultTyping.NON_FINAL, As.WRAPPER_ARRAY);
        // 关闭在反序列化时遇到未知属性的异常抛出
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 关闭将getter方法作为setter方法使用
        objectMapper.configure(MapperFeature.USE_GETTERS_AS_SETTERS, false);
        // 设置序列化时只包含非空的属性
        objectMapper.setSerializationInclusion(Include.NON_EMPTY);
        // 返回配置好的ObjectMapper实例
        return objectMapper;
    }

    /**
     * 自定义的ObjectMapper配置
     * <p>
     * buildObjectMapper方法允许用户自定义日期和时间格式，并创建一个ObjectMapper实例。
     *
     * @param datePattern 日期格式
     * @param timePattern 时间格式
     * @return ObjectMapper实例
     */

    public ObjectMapper buildObjectMapper(String datePattern, String timePattern) {
        ObjectMapper objectMapper = new ObjectMapper();
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        // 添加LocalDate类型的序列化和反序列化器
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(datePattern)));
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(datePattern)));
        // 添加LocalDateTime类型的序列化和反序列化器
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(timePattern)));
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(timePattern)));

        // 添加Date类型的自定义序列化和反序列化器，使用ThreadLocal来确保线程安全
        ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat(datePattern));

        // 添加Date类型的自定义序列化和反序列化器
        javaTimeModule.addSerializer(Date.class, new JsonSerializer<>() {
            @Override
            public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
                SimpleDateFormat formatter = dateFormatThreadLocal.get();
                String formattedDate = formatter.format(date);
                jsonGenerator.writeString(formattedDate);
            }
        });
        javaTimeModule.addDeserializer(Date.class, new JsonDeserializer<Date>() {
            @Override
            public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
                SimpleDateFormat format = dateFormatThreadLocal.get();
                String date = jsonParser.getText();
                try {
                    return format.parse(date);
                } catch (ParseException e) {
                    // 更好的异常处理，保留原始异常信息
                    throw new RuntimeException(e);
                }
            }
        });
        // 注册JavaTimeModule
        objectMapper.registerModule(javaTimeModule);
        // Long类型返回前端转为String类型，防止过长展示出错
        // 以下代码块已被注释，可根据需要取消注释
        // SimpleModule simpleModule = new SimpleModule();
        // simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        // simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
        // objectMapper.registerModule(simpleModule);
        // 允许接受空字符串作为null对象
        objectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
        // 关闭反序列化时遇到未知属性的异常抛出
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        return objectMapper;
    }


    /**
     * 将给定的对象转换成JSON格式的字符串
     *
     * @param object 待转换的对象，如果为null则直接返回null
     * @return 转换后的JSON字符串
     * @throws JsonConversionException 如果转换失败则抛出此异常
     */
    public String toJson(Object object) {
        // 如果对象为空，则直接返回null
        if (object == null) {
            return null;
        }
        // 如果对象是String类型的实例，则直接返回该对象（因为字符串本身就是有效的JSON）
        if (object instanceof String) {
            return (String) object;
        }
        try {
            // 尝试将对象转换为JSON格式的字符串
            return DEFAULT_OBJECT_MAPPER.writeValueAsString(object);
        } catch (IOException e) {
            // 如果转换过程中发生异常，则记录错误日志并抛出异常
            log.error("write to json string error:{}", object.getClass().getName(), e);
            throw new JsonConversionException(e);
        }
    }

    /**
     * 将json字符串转换为Map对象
     *
     * @param json 待转换的json字符串
     * @return 转换后的Map对象
     */
    public Map<String, Object> toMap(String json) {
        Map<String, Object> map = Maps.newHashMap();
        try {
            // 解析json字符串为JsonNode对象
            JsonNode rootNode = DEFAULT_OBJECT_MAPPER.readTree(json);
            // 获取JsonNode对象的所有字段名
            Iterator<String> fieldNames = rootNode.fieldNames();
            // 遍历所有字段名
            while (fieldNames.hasNext()) {
                String fieldName = fieldNames.next();
                // 获取字段名对应的JsonNode对象
                JsonNode jsonNode = rootNode.get(fieldName);
                // 如果JsonNode是值节点
                if (jsonNode.isValueNode()) {
                    // 将值节点的值转换为Object类型并添加到map中
                    map.put(fieldName, getNodeValue(jsonNode));
                    // 如果JsonNode是对象节点
                } else if (jsonNode.isObject()) {
                    // 递归调用toMap方法将对象节点转换为Map并添加到map中
                    map.put(fieldName, toMap(jsonNode.toString()));
                    // 如果JsonNode是数组节点
                } else if (jsonNode.isArray()) {
                    // 创建一个列表用于存储数组节点的值
                    List<Object> list = Lists.newArrayList();
                    // 遍历数组节点的每个子节点
                    for (JsonNode childNode : jsonNode) {
                        // 如果子节点是值节点
                        if (childNode.isValueNode()) {
                            // 将子节点的值转换为Object类型并添加到列表中
                            list.add(getNodeValue(childNode));
                            // 如果子节点是对象节点
                        } else if (childNode.isObject()) {
                            // 递归调用toMap方法将对象节点转换为Map并添加到列表中
                            list.add(toMap(childNode.toString()));
                        }
                    }
                    // 将列表添加到map中
                    map.put(fieldName, list);
                }
            }
            return map;
        } catch (JsonProcessingException e) {
            // 解析json字符串出错时打印错误日志
            log.error("parse json to map error:{}", json, e);
            throw new JsonConversionException(e);
        }
    }

    /**
     * 将json字符串转换为Map列表
     *
     * @param json 待转换的json字符串
     * @return 转换后的Map列表，如果转换失败则返回null
     */
    public List<Map<String, Object>> toMapList(String json) {
        try {
            List<Map<String, Object>> list = Lists.newArrayList();
            // 将json字符串解析为JsonNode对象
            JsonNode rootNode = DEFAULT_OBJECT_MAPPER.readTree(json);
            // 遍历JsonNode对象的子节点
            for (JsonNode childNode : rootNode) {
                // 如果子节点是对象节点
                if (childNode.isObject()) {
                    // 将子节点的字符串形式转换为Map对象，并添加到列表中
                    list.add(toMap(childNode.toString()));
                }
            }
            // 返回转换后的Map列表
            return list;
        } catch (JsonProcessingException e) {
            // 解析json字符串转换为Map列表时发生异常，记录错误日志
            log.error("parse json to map error:{}", json, e);
            throw new JsonConversionException(e);
        }
    }

    /**
     * 将对象转换成格式化的JSON字符串
     *
     * @param object 要转换的对象
     * @return 转换后的格式化JSON字符串
     */
    public String toJsonWithDefaultPrettyPrinter(Object object) {
        try {
            // 使用默认的格式化器将对象转换为JSON字符串
            return DEFAULT_OBJECT_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(object);
        } catch (IOException e) {
            // 如果转换过程中发生IO异常，则记录错误日志并返回空字符串
            log.error("write to json string error:{}", object, e);
            throw new JsonConversionException(e);
        }
    }

    /**
     * 将给定的对象转换为指定类型的对象
     *
     * @param object 要转换的对象
     * @param clazz  转换后对象的类型
     * @param <T>    转换后对象的类型
     * @return 转换后的对象
     */
    public <T> T toObject(Object object, Class<T> clazz) {
        if (object == null) {
            return null;
        }
        // 兼容Java 16之前的版本，并避免直接转型可能引发的ClassCastException
        if (!(object instanceof String json)) {
            if (clazz.isInstance(object)) {
                return clazz.cast(object);
            }
            throw new IllegalArgumentException("Object type does not match the specified class.");
        }
        if (json.isEmpty()) {
            return null;
        }
        // 检查DEFAULT_OBJECT_MAPPER是否为null
        if (DEFAULT_OBJECT_MAPPER == null) {
            throw new IllegalStateException("DEFAULT_OBJECT_MAPPER is null.");
        }
        try {
            return DEFAULT_OBJECT_MAPPER.readValue(json, clazz);
        } catch (IOException e) {
            log.error("Parse json string error: {}", json, e);
            // 重新抛出异常，以便调用者能够处理它
            throw new JsonConversionException(e);
        }
    }

    /**
     * 将给定的对象转换为指定类型的对象
     *
     * @param object   待转换的对象
     * @param javaType 转换后对象的类型
     * @param <T>      转换后对象的类型
     * @return 转换后的对象
     * @throws JsonConversionException 如果解析JSON字符串时发生异常
     */
    public <T> T toObject(Object object, JavaType javaType) {
        if (object == null) {
            return null;
        }
        String jsonString = null;
        if (!(object instanceof String)) {
            // 如果object不是String类型且不能直接转换为T，则抛出异常或进行其他处理
            throw new IllegalArgumentException("Object is not a JSON string and cannot be directly cast to type T.");
        }
        jsonString = (String) object;
        if (jsonString.isEmpty()) {
            return null;
        }
        try {
            // 确保DEFAULT_OBJECT_MAPPER已初始化
            if (DEFAULT_OBJECT_MAPPER == null) {
                throw new IllegalStateException("DEFAULT_OBJECT_MAPPER has not been initialized.");
            }
            return DEFAULT_OBJECT_MAPPER.readValue(jsonString, javaType);
        } catch (IOException e) {
            log.error("Parse JSON string error: {}", jsonString, e);
            throw new JsonConversionException(e);
        }
    }

    /**
     * 将对象转换成指定类型的列表
     *
     * @param object   待转换的对象
     * @param beanType 转换后列表中元素的类型
     * @param <T>      转换后列表中元素的类型
     * @return 转换后的列表
     */
    public <T> List<T> toList(Object object, Class<T> beanType) {
        // 如果待转换的对象为空，则返回空列表
        if (object == null) {
            return new ArrayList<>();
        }

        // 判断对象是否为字符串类型
        if (object instanceof String) {
            String jsonString = (String) object;
            // 如果jsonString为空字符串或不是有效的JSON，则返回空列表
            if (jsonString.isEmpty() || !isValidJson(jsonString)) {
                return new ArrayList<>();
            }
            // 构造参数化类型，用于指定列表中元素的类型
            JavaType javaType = DEFAULT_OBJECT_MAPPER.getTypeFactory().constructParametricType(List.class, beanType);
            try {
                // 使用ObjectMapper的readValue方法将jsonString转换为指定类型的列表
                return DEFAULT_OBJECT_MAPPER.readValue(jsonString, javaType);
            } catch (IOException e) {
                // 如果转换过程中发生异常，则记录错误日志并返回空列表
                log.error("Translate to POJO failed. jsonString={}", jsonString, e);
                return new ArrayList<>();
            }
        } else if (object instanceof List) {
            // 如果对象本身就是List类型，则进行类型转换并返回
            return (List<T>) object;
        } else {
            // 如果对象既不是字符串也不是List，则返回空列表或抛出异常（根据业务需求决定）
            log.warn("Unsupported object type for conversion to list: {}", object.getClass().getName());
            // 或者抛出异常，如 throw new IllegalArgumentException("Unsupported object type for conversion to list.");
            throw new IllegalArgumentException("Unsupported object type for conversion to list.");
        }
    }

    /**
     * 用于检查字符串是否为有效的JSON（简单实现，可能需要根据实际需求进行扩展）
     */
    private boolean isValidJson(String json) {
        try {
            DEFAULT_OBJECT_MAPPER.readTree(json);
            return true;
        } catch (IOException e) {
            log.error("Invalid JSON: {}", json, e);
            return false;
        }
    }

    /**
     * 将JSON字符串转换为POJO对象
     *
     * @param object      待转换的JSON字符串
     * @param typeReference 目标POJO对象的类型引用
     * @param <T>           目标POJO对象的类型
     * @return 转换后的POJO对象，若转换失败则返回null
     */
    public <T> T toPojo(Object object, TypeReference<T> typeReference) {
        if (object == null) {
            return null;
        }
        if (!(object instanceof String json)) {
            return (T) object;
        }
        if (json.isEmpty()) {
            return null;
        } else {
            try {
                return DEFAULT_OBJECT_MAPPER.readValue(json, typeReference);
            } catch (IOException e) {
                log.error("parse json string error:" + json, e);
                return null;
            }
        }
    }

    /**
     * 判断字符串是否为JSON格式。
     *
     * @param str 待判断的字符串
     * @return 若字符串以"{"开头且以"}"结尾则返回true，否则返回false。
     */
    public boolean isJson(String str) {
        if (str == null) {
            // 处理null值的情况，避免NullPointerException
            return false;
        }
        // 判断字符串是否以"{"开头且以"}"结尾
        return str.startsWith("{") && str.endsWith("}");
    }

    /**
     * 判断给定的字符串是否为JSON数组格式
     *
     * @param str 待判断的字符串
     * @return 如果字符串以 "[" 开头，并以 "]" 结尾，返回true；否则返回false
     */
    public boolean isJsonArray(String str) {
        // 判断字符串是否以 "[" 开头
        // 如果以 "[" 开头，则继续判断字符串是否以 "]" 结尾
        // 如果同时满足两个条件，则返回true，否则返回false
        return str != null && str.startsWith("[") && str.endsWith("]");
    }

    /**
     * 构建集合类型的JavaType对象
     *
     * @param collectionClass 集合类class
     * @param elementClass    元素类class
     * @return 集合类型的JavaType对象
     */
    public JavaType buildCollectionType(Class<? extends Collection> collectionClass, Class<?> elementClass) {
        // 使用DEFAULT_OBJECT_MAPPER的getTypeFactory()方法获取TypeFactory对象
        // 调用TypeFactory对象的constructCollectionType()方法，传入集合类class和元素类class
        // 构建并返回集合类型的JavaType对象
        return DEFAULT_OBJECT_MAPPER.getTypeFactory().constructCollectionType(collectionClass, elementClass);
    }

    /**
     * 构建指定类型的Map的JavaType对象
     *
     * @param mapClass   指定的Map类型
     * @param keyClass   Map中键的类型
     * @param valueClass Map中值的类型
     * @return 返回指定类型的Map的JavaType对象
     */
    public JavaType buildMapType(Class<? extends Map> mapClass, Class<?> keyClass, Class<?> valueClass) {
        // 调用DEFAULT_OBJECT_MAPPER的getTypeFactory()方法获取TypeFactory对象
        // 调用TypeFactory对象的constructMapType()方法构建指定类型的Map的JavaType对象
        // 构造方法的参数分别为mapClass（指定的Map类型）、keyClass（Map中键的类型）和valueClass（Map中值的类型）
        return DEFAULT_OBJECT_MAPPER.getTypeFactory().constructMapType(mapClass, keyClass, valueClass);
    }

    /**
     * 使用给定的JSON字符串更新指定对象。
     *
     * @param jsonString 用于更新对象的JSON字符串
     * @param object     要更新的对象
     * @throws IOException 如果在解析JSON字符串时出现错误，则抛出IOException
     */
    public void update(String jsonString, Object object) {
        try {
            // 使用默认的对象映射器创建一个读取器，用于更新指定的对象
            DEFAULT_OBJECT_MAPPER.readerForUpdating(object).readValue(jsonString);
        } catch (IOException e) {
            // 如果在解析JSON字符串时出现错误，则记录错误日志
            log.error("update json string: {} =>to object: {} ", jsonString, object, e);
            throw new JsonConversionException(e);
        }
    }

    /**
     * 从JsonNode中获取节点值
     *
     * @param childNode 需要获取值的JsonNode对象
     * @return 返回节点值，如果节点是BigDecimal类型，则返回BigDecimal对象；
     * 如果节点是Double类型，则返回Double对象；
     * 如果节点是Float类型，则返回Float对象；
     * 如果节点是Long类型，则返回Long对象；
     * 如果节点是Int类型，则返回Integer对象；
     * 如果节点是Boolean类型，则返回Boolean对象；
     * 如果节点是文本类型，则返回文本字符串；
     * 如果节点是其他类型，则返回null。
     */
    private static Object getNodeValue(JsonNode childNode) {
        // 防止空指针异常
        if (childNode == null) {
            return null;
        }
        // 如果节点是BigDecimal类型
        if (childNode.isBigDecimal()) {
            // 返回BigDecimal对象
            return childNode.decimalValue();
        }
        // 如果节点是Double类型
        if (childNode.isDouble()) {
            // 返回Double对象
            return childNode.asDouble();
        }
        // 如果节点是Float类型
        if (childNode.isFloat()) {
            // 返回Float对象
            return childNode.floatValue();
        }
        // 如果节点是Long类型
        if (childNode.isLong()) {
            // 返回Long对象
            return childNode.asLong();
        }
        // 如果节点是Int类型
        if (childNode.isInt()) {
            // 返回Integer对象
            return childNode.asInt();
        }
        // 如果节点是Boolean类型
        if (childNode.isBoolean()) {
            // 返回Boolean对象
            return childNode.asBoolean();
        }
        // 返回文本字符串
        return childNode.asText();
    }

    private class BaseSimpleDateFormat extends SimpleDateFormat {
        /**
         * 使用默认日期时间格式"yyyy-MM-dd HH:mm:ss.SSS"初始化BaseSimpleDateFormat对象。
         */
        public BaseSimpleDateFormat() {
            super("yyyy-MM-dd HH:mm:ss.SSS");
        }

        /**
         * 使用指定的日期时间格式初始化BaseSimpleDateFormat对象。
         *
         * @param pattern 自定义的日期时间格式字符串
         */
        public BaseSimpleDateFormat(String pattern) {
            super(pattern);
        }

        /**
         * 重写父类的parse方法，用于解析字符串为日期对象。
         *
         * @param source 要解析的日期字符串，格式为yyyy-MM-dd HH:mm:ss或yyyy-MM-dd HH:mm:ss.SSS
         * @return 解析后的日期对象
         * @throws ParseException 如果解析字符串时发生错误，则抛出ParseException异常
         */
        @Override
        public Date parse(String source) throws ParseException {
            // 如果源字符串长度为19，即格式为yyyy-MM-dd HH:mm:ss
            if (source.length() == 19) {
                // 在字符串末尾添加".000"，使其符合yyyy-MM-dd HH:mm:ss.SSS的格式
                source = source.concat(".000");
            }
            // 调用父类的parse方法解析修改后的字符串为日期对象，并返回
            return super.parse(source);
        }
    }
}